#pragma once

#include "nocopy.hpp"
#include "Log.hpp"
#include <cerrno> //包含了与错误处理相关的宏和符号常量，特别是errno全局变量
#include <cstring>
#include <sys/epoll.h> //包含了用于epoll I/O 多路复用机制的定义和类型

class Epoller : public nocopy
{
    static const int size = 128;

public:
    Epoller()
    {
        _epfd = epoll_create(size); // epoll模型创建后
        if (_epfd == -1)
        {
            lg(Error, "epoll_create error: %s", strerror(errno));
        }
        else
        {
            lg(Info, "epoll_creat success:%d", _epfd);
        }
    }

    // 等待就绪事件，就绪文件描述符的事件存放在就绪队列，取到revents数组
    int EpollerWait(struct epoll_event revents[], int num) // revents用于存储就绪事件
    {
        int n = epoll_wait(_epfd, revents, num, -1); //-1表示非阻塞等待，num是revents数组的大小
        return n;
    }

    // 对存放在红黑树中的要监听的文件描述符与应事件的节点进行修改，oper表示操作，sock为要监听的套接字，event为要关心的事件
    int EpollerUpdate(int oper, int sock, uint32_t event)
    {
        int n = 0;
        if (oper == EPOLL_CTL_DEL)
        {
            n = epoll_ctl(_epfd, oper, sock, nullptr); // nullptr表示无需再监听该fd
            if (n != 0)                                // epoll_ctl()操作成功返回0
            {
                lg(Error, "epoll_ctl delete error!");
            }
        }
        else // EPOLL_CTL_MOD || EPOLL_CTL_ADD
        {
            struct epoll_event ev; // epoll模型中描述文件描述符事件的结构体
            ev.events = event;     // 要关心的事件
            ev.data.fd = sock;     // 要关心的套接字

            n = epoll_ctl(_epfd, oper, sock, &ev);
            if (n != 0)
            {
                lg(Error, "epoll_ctl error!");
            }
        }
        return n;
    }

    ~Epoller()
    {
        if (_epfd >= 0)
            close(_epfd);//关闭epoll模型的fd
    }

private:
    int _epfd;          // epoll模型在文件中，也需要一个文件描述符
    int _timeout{3000}; // 无事件就绪，每三秒返回一次
};